<?php

namespace Mtt\BlogBundle\Entity\Repository;

use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\ORM\Query;
use Doctrine\Persistence\ManagerRegistry;
use Mtt\BlogBundle\Entity\Category;

/**
 * CategoryRepository
 *
 * This class was generated by the Doctrine ORM. Add your own custom
 * repository methods below.
 *
 * @method Category[] findAll()
 */
class CategoryRepository extends ServiceEntityRepository
{
    /**
     * @param ManagerRegistry $registry
     */
    public function __construct(ManagerRegistry $registry)
    {
        parent::__construct($registry, Category::class);
    }

    /**
     * @return Query
     */
    public function getListQuery(): Query
    {
        $qb = $this->createQueryBuilder('e');

        return $qb->orderBy('e.name', 'ASC')
            ->getQuery();
    }

    /**
     * @return array
     */
    public function getNamesArray(): array
    {
        $qb = $this->createQueryBuilder('c')
            ->select('c.id', 'c.name', 'c.nestedSet.depth AS depth')
            ->orderBy('c.nestedSet.leftKey', 'ASC');

        return array_map(
            function ($el) {
                if ($el['depth'] > 1) {
                    $el['name'] = str_repeat('_ ', $el['depth'] - 1) . $el['name'];
                }

                return $el;
            },
            $qb->getQuery()->getArrayResult()
        );
    }

    /**
     * @param Category $entity
     * @param int $index
     * @param int $depth
     *
     * @return void
     */
    public function addToTree(Category $entity, int $index, int $depth): void
    {
        $qb0 = $this->createQueryBuilder('c');
        $qb0->update()
            ->set('c.nestedSet.leftKey', 'c.nestedSet.leftKey + 2')
            ->where($qb0->expr()->gte('c.nestedSet.leftKey', ':idx'))
            ->andWhere($qb0->expr()->isNotNull('c.nestedSet.leftKey'))
            ->setParameter('idx', $index)
            ->getQuery()
            ->execute()
        ;

        $qb1 = $this->createQueryBuilder('c');
        $qb1->update()
            ->set('c.nestedSet.rightKey', 'c.nestedSet.rightKey + 2')
            ->where($qb1->expr()->gte('c.nestedSet.rightKey', ':idx'))
            ->andWhere($qb1->expr()->isNotNull('c.nestedSet.leftKey'))
            ->setParameter('idx', $index)
            ->getQuery()
            ->execute()
        ;

        $ns = $entity->getNestedSet();
        $ns
            ->setLeftKey($index)
            ->setRightKey($index + 1)
            ->setDepth($depth)
        ;

        $this->getEntityManager()->flush();
    }

    /**
     * @param Category $entity
     *
     * @return int
     */
    public function findNeighbourKey(Category $entity): int
    {
        $qb = $this->createQueryBuilder('c');
        $qb
            ->select('COALESCE(MAX(c.nestedSet.rightKey), 0)')
            ->where($qb->expr()->lt('c.name', ':name'))
            ->setParameter('name', $entity->getName())
        ;

        if ($entity->getParent()) {
            $qb
                ->andWhere($qb->expr()->eq('c.parent', ':parent'))
                ->setParameter('parent', $entity->getParent()->getId())
            ;
        } else {
            $qb
                ->andWhere($qb->expr()->isNull('c.parent'))
            ;
        }

        $key = (int)$qb->getQuery()->getSingleScalarResult();
        if ($key == 0 && $entity->getParent()) {
            $key = $entity->getParent()->getNestedSet()->getLeftKey();
        }

        return $key;
    }

    public function save(Category $entity)
    {
        $new = is_null($entity->getId());

        $this->getEntityManager()->persist($entity);
        $this->getEntityManager()->flush();

        if ($new) {
            $parent = $entity->getParent();
            if ($parent) {
                $nsParent = $parent->getNestedSet();

                if ($nsParent->getRightKey() - $nsParent->getLeftKey() == 1) {
                    $index = $nsParent->getRightKey();
                } else {
                    $index = 1 + $this->findNeighbourKey($entity);
                }
                $depth = $nsParent->getDepth() + 1;
            } else {
                $index = 1 + $this->findNeighbourKey($entity);
                $depth = 1;
            }

            $this->addToTree($entity, $index, $depth);
        }
    }
}
